#include <stdlib.h>
#include "Arbre.h"
#include "Symbole.h"
#include "Valeur.h"
#include "SymboleValue.h"
#include <vector>

using namespace std;

////////////////////////////////////////////////////////////////////////////////
// NoeudSeqInst
////////////////////////////////////////////////////////////////////////////////

NoeudSeqInst::NoeudSeqInst() : tabInst() {
}

////////////////////////////////////////////////////////////////////////////////

Valeur* NoeudSeqInst::getValeur() {
    Valeur* v = NULL;

        for (unsigned int i = 0; i < tabInst.size(); i++)
            v = tabInst[i]->getValeur(); // on evalue chaque instruction de la séquence

    return v; // par convention, resultat = valeur de la derniere instruction

}

////////////////////////////////////////////////////////////////////////////////

void NoeudSeqInst::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Sequence de " << tabInst.size() << " instruction(s)" << endl;
    for (unsigned int i = 0; i < tabInst.size(); i++)
    {
        tabInst[i]->afficher(indentation + 1); // on affiche les fils en augmentant l'indentation
        cout << endl;
    }
}

////////////////////////////////////////////////////////////////////////////////

void NoeudSeqInst::ajouteInstruction(Noeud* instruction) {
    tabInst.push_back(instruction);
}

////////////////////////////////////////////////////////////////////////////////

void NoeudSeqInst::tradC(fstream &fichier) {

    for (unsigned int i = 0; i < tabInst.size(); i++){
        tabInst[i]->tradC(fichier); // on affiche la traduction des fils 
    fichier << " ;" << endl;}
    

}

////////////////////////////////////////////////////////////////////////////////
// NoeudAffectation
////////////////////////////////////////////////////////////////////////////////

NoeudAffectation::NoeudAffectation(Noeud* variable, Noeud* expression) {
    this->variable = variable;
    this->expression = expression;
}

////////////////////////////////////////////////////////////////////////////////

Valeur* NoeudAffectation::getValeur() {
    Valeur* valeur = expression->getValeur(); // on évalue l'expression
    ((SymboleValue*) variable)->setValeur(valeur); // on affecte la variable
    return valeur; // par convention, une affectation a pour valeur la valeur affectée
}

////////////////////////////////////////////////////////////////////////////////

void NoeudAffectation::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Affectation" << endl;
    variable->afficher(indentation + 1); // on affiche variable et expression
    expression->afficher(indentation + 1); // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
void NoeudAffectation::tradC(fstream &fichier) {
    
    static vector<SymboleValue> tab;
            Valeur* val =  variable->getValeur();

 unsigned int i = 0;
 int exist = 0; 
 while (i<tab.size() && (exist == 0) ){
     
     if(tab[i]== (((SymboleValue*)variable)->getChaine()))
             exist = 1 ;
     i++;
 }
 
 if (exist == 0)
 {  
       if (typeid (*val) == typeid (ValeurEntiere)){
                fichier << "int ";
        }
        else if (typeid (*val) == typeid (ValeurChaine)){
                fichier << "char ";
         }
        if (typeid (*val) == typeid (ValeurReelle)){
                fichier << "float ";
        }   
            tab.push_back(*((SymboleValue*)variable));
 }


    
   
    
    variable->tradC(fichier);
    if (exist==0 && typeid (*val) == typeid (ValeurChaine))
        fichier<<"[]";
    fichier << " = ";
    expression->tradC(fichier);
    
}
////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurBinaire
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurBinaire::NoeudOperateurBinaire(Symbole operateur,
        Noeud* operandeGauche,
        Noeud* operandeDroit) {
    this->operateur = operateur;
    this->operandeGauche = operandeGauche;
    this->operandeDroit = operandeDroit;
}

////////////////////////////////////////////////////////////////////////////////         

Valeur* NoeudOperateurBinaire::getValeur() {
    Valeur* valeur = NULL;

    Valeur* od = operandeDroit->getValeur();
    if (operandeGauche != NULL){
            Valeur* og = operandeGauche->getValeur();
        
   
        if (((SymboleValue*)operandeGauche)->estDefini() && ((SymboleValue*)operandeDroit)->estDefini())
        {
            if (typeid (*og) == typeid (ValeurEntiere) && typeid (*od) == typeid (ValeurEntiere)) {
                if (this->operateur == "+") valeur = new ValeurEntiere(((ValeurEntiere*) og)->getValeur()+((ValeurEntiere*) od)->getValeur());
                else if (this->operateur == "-") valeur = new ValeurEntiere(((ValeurEntiere*) og)->getValeur()-((ValeurEntiere*) od)->getValeur());
                else if (this->operateur == "*") valeur = new ValeurEntiere(((ValeurEntiere*) og)->getValeur()*((ValeurEntiere*) od)->getValeur());
                else if (this->operateur == "==") valeur = new ValeurEntiere(((ValeurEntiere*) og)->getValeur() == ((ValeurEntiere*) od)->getValeur());
                else if (this->operateur == "!=") valeur = new ValeurEntiere(((ValeurEntiere*) og)->getValeur() != ((ValeurEntiere*) od)->getValeur());
                else if (this->operateur == "<") valeur = new ValeurEntiere(((ValeurEntiere*) og)->getValeur()<((ValeurEntiere*) od)->getValeur());
                else if (this->operateur == "<=") valeur = new ValeurEntiere(((ValeurEntiere*) og)->getValeur() <= ((ValeurEntiere*) od)->getValeur());
                else if (this->operateur == ">") valeur = new ValeurEntiere(((ValeurEntiere*) og)->getValeur()>((ValeurEntiere*) od)->getValeur());
                else if (this->operateur == ">=") valeur = new ValeurEntiere(((ValeurEntiere*) og)->getValeur() >= ((ValeurEntiere*) od)->getValeur());
                else if (this->operateur == "et") valeur = new ValeurEntiere(((ValeurEntiere*) og)->getValeur() && ((ValeurEntiere*) od)->getValeur());
                else if (this->operateur == "ou") valeur = new ValeurEntiere(((ValeurEntiere*) og)->getValeur() || ((ValeurEntiere*) od)->getValeur());

                else // this->operateur=="/"
                  
                    try {
                        if (((ValeurEntiere*) od)->getValeur() == 0) throw 0;
                        else valeur = new ValeurEntiere(((ValeurEntiere*) og)->getValeur() / ((ValeurEntiere*) od)->getValeur());
                    } catch (int) {
                        cout << "Exception: division par zéro." << endl;
                        valeur = new ValeurEntiere(((ValeurEntiere*) og)->getValeur());
                    }
            } else if (typeid (*og) == typeid (ValeurReelle) && typeid (*od) == typeid (ValeurReelle)) {
                if (this->operateur == "+") valeur = new ValeurReelle(((ValeurReelle*) og)->getValeur()+((ValeurReelle*) od)->getValeur());
                else if (this->operateur == "-") valeur = new ValeurReelle(((ValeurReelle*) og)->getValeur()-((ValeurReelle*) od)->getValeur());
                else if (this->operateur == "*") valeur = new ValeurReelle(((ValeurReelle*) og)->getValeur()*((ValeurReelle*) od)->getValeur());
                else if (this->operateur == "==") valeur = new ValeurReelle(((ValeurReelle*) og)->getValeur() == ((ValeurReelle*) od)->getValeur());
                else if (this->operateur == "!=") valeur = new ValeurReelle(((ValeurReelle*) og)->getValeur() != ((ValeurReelle*) od)->getValeur());
                else if (this->operateur == "<") valeur = new ValeurReelle(((ValeurReelle*) og)->getValeur()<((ValeurReelle*) od)->getValeur());
                else if (this->operateur == "<=") valeur = new ValeurReelle(((ValeurReelle*) og)->getValeur() <= ((ValeurReelle*) od)->getValeur());
                else if (this->operateur == ">") valeur = new ValeurReelle(((ValeurReelle*) og)->getValeur()>((ValeurReelle*) od)->getValeur());
                else if (this->operateur == ">=") valeur = new ValeurReelle(((ValeurReelle*) og)->getValeur() >= ((ValeurReelle*) od)->getValeur());
                else if (this->operateur == "et") valeur = new ValeurReelle(((ValeurReelle*) og)->getValeur() && ((ValeurReelle*) od)->getValeur());
                else if (this->operateur == "ou") valeur = new ValeurReelle(((ValeurReelle*) og)->getValeur() || ((ValeurReelle*) od)->getValeur());
                else {if (((ValeurReelle*) od)->getValeur() == 0) throw 0;
                    else valeur = new ValeurReelle(((ValeurReelle*) og)->getValeur() / ((ValeurReelle*) od)->getValeur());}
                // this->operateur=="/"
                    /*if (od!=0)
                            valeur = new ValeurReelle(((ValeurReelle*)og)->getValeur()/((ValeurReelle*)od)->getValeur());
                    else {
                            cout << "Erreur pendant l'interpretation : division par zero" << endl;
                            exit(0); // plus tard on levera une exception
                    }*/
                    try {
                    if (((ValeurReelle*) od)->getValeur() == 0) throw 0;
                else valeur = new ValeurReelle(((ValeurReelle*) og)->getValeur() / ((ValeurReelle*) od)->getValeur());
                    } catch (float) {
                        cout << "Exception: division par zéro." << endl;
                        valeur = new ValeurEntiere(((ValeurReelle*) og)->getValeur());
                    }
            } else if (typeid (*og) == typeid (ValeurChaine) && typeid (*od) == typeid (ValeurChaine)) {
                if (this->operateur == "==") valeur = new ValeurReelle(((ValeurChaine*) og)->getValeur() == ((ValeurChaine*) od)->getValeur());
                else if (this->operateur == "!=") valeur = new ValeurReelle(((ValeurChaine*) og)->getValeur() != ((ValeurChaine*) od)->getValeur());



            }                         
        }
    }
    else {
        if (((SymboleValue*)operandeDroit)->estDefini())
            {
                if (typeid (*od) == typeid (ValeurEntiere)) {
                    if (this->operateur == "-") valeur = new ValeurEntiere(0 - ((ValeurEntiere*) od)->getValeur());
                    else if (this->operateur == "non") 
                        {if (((ValeurEntiere*)od)->getValeur()==0) valeur = new ValeurEntiere(1);
                         else                                       valeur = new ValeurEntiere(0);}
                
                else if (typeid (*od) == typeid (ValeurReelle)){
                    if (this->operateur == "-") valeur = new ValeurReelle(0.0 - ((ValeurReelle*) od)->getValeur());
                    else if (this->operateur == "non") 
                    {if (((ValeurReelle*) od)->getValeur()==0.0)
                        valeur = new ValeurReelle(1.0);
                    else valeur = new ValeurReelle(0.0);}}
                
        
         }
}
}
    return valeur;
}

////////////////////////////////////////////////////////////////////////////////

void NoeudOperateurBinaire::afficher(unsigned short indentation) {
    
    if (operateur.getChaine() != "non" && operateur.getChaine() != "-")
    {
        Noeud::afficher(indentation);
        cout << "Noeud - Operateur Binaire \"" << this->operateur.getChaine() << "\" applique a : " << endl;
        operandeGauche->afficher(indentation + 1); // on affiche fils gauche et fils droit
        operandeDroit->afficher(indentation + 1); // en augmentant l'indentation
    }
    else 
    { 
        Noeud::afficher(indentation);
        cout << "Noeud - Operateur Binaire \"" << this->operateur.getChaine() << "\" applique a : " << endl;
        operandeDroit->afficher(indentation + 1); 
    }

}

////////////////////////////////////////////////////////////////////////////////

void NoeudOperateurBinaire::tradC(fstream &fichier) {
    if (operateur.getChaine() != "non" && operateur.getChaine() != "-")
    {
        operandeGauche->tradC(fichier);
        fichier<< " " <<operateur.getChaine();
        fichier<<" ";
        operandeDroit->tradC(fichier);
    }
    else 
    { 
        fichier<< operateur.getChaine();
        fichier<<" ";
        operandeDroit->tradC(fichier);
    }
}



////////////////////////////////////////////////////////////////////////////////

NoeudSi::NoeudSi() {

}



////////////////////////////////////////////////////////////////////////////////

Valeur* NoeudSi::getValeur() {
    unsigned int i = 0;
    while (i < vectCond.size() && vectCond[i] != NULL && !vectCond[i]->getValeur()) {
        i++;
    }

    if (i < vectCond.size())
        vectSeqInst[i]->getValeur();
    return (new Valeur);
}


////////////////////////////////////////////////////////////////////////////////

void NoeudSi::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Si" << endl;
    int i=0;
    while (i < vectCond.size() && vectCond[i] != NULL) {
        vectCond[i]->afficher(indentation+1);
        vectSeqInst[i]->afficher(indentation+1);
        i++;
    }
    if (vectCond.size()<vectSeqInst.size()){
        vectSeqInst[i]->afficher(indentation+1);

    }
    
}


////////////////////////////////////////////////////////////////////////////////

void NoeudSi::ajoute(Noeud* cond, Noeud* seq) {
    vectCond.push_back(cond);
    vectSeqInst.push_back(seq);
}

////////////////////////////////////////////////////////////////////////////////

void NoeudSi::tradC(fstream &fichier) {
    fichier << "if (";

    vectCond[0]->tradC(fichier);
    fichier<<" ) {"<<endl;
    vectSeqInst[0]->tradC(fichier);
    fichier<<"}";
    
    int i =1;
    if (vectCond.size()>1){
   
            while ((i < (vectCond.size()+1)) && (vectCond[i] != NULL )) {
                fichier<<"else if (";
                vectCond[i]->tradC(fichier);
                fichier<<" ) {"<<endl;
                vectSeqInst[1]->tradC(fichier);
                fichier<<"}";
                i++;
          }
    }
    if (vectCond.size()<vectSeqInst.size()){
        fichier <<"else  { "<<endl; 
        vectSeqInst[i]->tradC(fichier);
        fichier<<"}";
    }
}


////////////////////////////////////////////////////////////////////////////////

NoeudTq::NoeudTq(Noeud* condition, Noeud* seqTq) {
    this->condition = condition;
    this->seqTq = seqTq;
}

////////////////////////////////////////////////////////////////////////////////

void NoeudTq::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Tant que" << endl;
    condition->afficher(indentation + 1);
    seqTq->afficher(indentation + 1);
}

////☃////////////////////////////////////////////////////////////////////////////

Valeur* NoeudTq::getValeur() {
    while (((ValeurEntiere*)(condition->getValeur()))->getValeur()) {
        seqTq->getValeur();
    }
    return NULL;
}

////////////////////////////////////////////////////////////////////////////////

void NoeudTq::tradC(fstream &fichier) {
    fichier << "while ( ";
    condition->tradC(fichier);
    fichier << " )  {" << endl;
    seqTq->tradC(fichier);
    fichier << "}" ;
}



////////////////////////////////////////////////////////////////////////////////

NoeudRepeter::NoeudRepeter(Noeud* seqRep, Noeud* condition) {
    this->condition = condition;
    this->seqRep = seqRep;
}

////////////////////////////////////////////////////////////////////////////////

void NoeudRepeter::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Tant que" << endl;
    condition->afficher(indentation + 1);
    seqRep->afficher(indentation + 1);
}

////☃////////////////////////////////////////////////////////////////////////////

Valeur* NoeudRepeter::getValeur() {

    do {
        seqRep->getValeur();
    } while (((ValeurEntiere*)(condition->getValeur()))->getValeur());
    return NULL;
}

////////////////////////////////////////////////////////////////////////////////

void NoeudRepeter::tradC(fstream &fichier) {

    fichier << "do {" << endl;
    seqRep->tradC(fichier);
    fichier<<"} while ( ";
    condition->tradC(fichier);
    fichier<<" )"<<endl;
}

////////////////////////////////////////////////////////////////////////////////

NoeudPour::NoeudPour(Noeud* affectation, Noeud* condition, Noeud* affectationBis, Noeud* seqPour) {
    this->affectation = affectation;
    this->condition = condition;
    this->affectationBis = affectationBis;
    this->seqPour = seqPour;
}

////////////////////////////////////////////////////////////////////////////////

void NoeudPour::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Pour" << endl;
    condition->afficher(indentation + 1);
    seqPour->afficher(indentation + 1);
}

////////////////////////////////////////////////////////////////////////////////

void NoeudPour::tradC(fstream &fichier) {
    fichier<<"for ( ";
    affectation->tradC(fichier);
    fichier<<" ; ";
    condition->tradC(fichier);
    fichier<<" ; ";
    affectationBis->tradC(fichier);
    fichier<<" ) {"<<endl;
    seqPour->tradC(fichier);
    fichier<<"}"<<endl;
    
}


////☃////////////////////////////////////////////////////////////////////////////

Valeur* NoeudPour::getValeur() {

    affectation->getValeur();
    while (((ValeurEntiere*)(condition->getValeur()))->getValeur()) {
        seqPour->getValeur();        
        affectationBis->getValeur();
    }
    return NULL;
}




////////////////////////////////////////////////////////////////////////////////

NoeudLire::NoeudLire(Noeud* variable) {
    this->variable = variable;
}

////////////////////////////////////////////////////////////////////////////////

void NoeudLire::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Lire" << endl;
    variable->afficher(indentation + 1);
}


////////////////////////////////////////////////////////////////////////////////

void NoeudLire::tradC(fstream &fichier) {
    

    Valeur* val =  variable->getValeur();
    

   if (typeid (*val) == typeid (ValeurEntiere)){
        fichier << "scanf ( \"%d\" , &";
    }
    else if (typeid (*val) == typeid (ValeurChaine)){
        fichier << "scanf ( \"%s\" , &";
    }
    else if (typeid (*val) == typeid (ValeurReelle)){
        fichier << "scanf ( \"%f\" , &";
    }

    variable->tradC(fichier);
    fichier<<" )";
  }

////☃////////////////////////////////////////////////////////////////////////////

Valeur* NoeudLire::getValeur() {                                                                    
    Valeur* var;
    string s; 
    cin >> s;

        if (isdigit(s[0]))
          
        {   int i=1;int lim = s.length();
            while (isdigit(s[i]) && i < lim-1)
            i++;
            if (s[i]=='.'){
                var= new ValeurReelle((float)atoi(s.c_str()));
            }
                else 
                    var = new ValeurEntiere(atoi(s.c_str()));
        }
        else {
    var = new ValeurChaine(s);
        }


    ((SymboleValue*) variable)->setValeur(var); // on affecte la variable

    return NULL;
}



////////////////////////////////////////////////////////////////////////////////

NoeudEcrire::NoeudEcrire(Noeud* string) {
    this->chaine = string;
}

////////////////////////////////////////////////////////////////////////////////

void NoeudEcrire::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Ecrire" << endl;
    chaine->afficher(indentation + 1);
}


////////////////////////////////////////////////////////////////////////////////

void NoeudEcrire::tradC(fstream &fichier) {
 
    
    
   Valeur* val =  chaine->getValeur();
   
   if (*((SymboleValue*) chaine) =="<VARIABLE>")
   {
    
   if (typeid (*val) == typeid (ValeurEntiere)){
        fichier << "printf ( \"%d\" , ";
        chaine->tradC(fichier);
        fichier<<" )";

    }
   
   
    else if (typeid (*val) == typeid (ValeurChaine)){
        fichier << "printf ( ";
        fichier << (*(SymboleValue*)chaine).getChaine();
//        fichier << (*((ValeurChaine*)chaine->getValeur())).getValeur();
        fichier <<" )";
        
//        
//        string w = (*((ValeurChaine*) chaine->getValeur())).getValeur();
//        string x = "";
//        for (int i=0; i<w.length();i++)
//        {
//            if (w[i] != '"')
//            {
//                x.push_back(w[i]);
//            }
//        }
//        
//        Noeud* tmp = (Valeur*)new ValeurChaine(x);
//        tmp->tradC(fichier);
                
    }
    else if (typeid (*val) == typeid (ValeurReelle)){
        fichier << "printf ( \"%f\" , ";
        chaine->tradC(fichier);
        fichier<<" )";
    }
   

   }
   else
   {
       if (*((SymboleValue*) chaine) !="<STRING>")
        {
            fichier << "printf ( \" ";
            chaine->tradC(fichier);
            fichier<<"\" )";
        }
       else 
        {
            fichier << "printf ( ";
            chaine->tradC(fichier);
            fichier<<" )";
        }
   }
}

////☃////////////////////////////////////////////////////////////////////////////

Valeur* NoeudEcrire::getValeur() {
    
    


        Valeur* val = ( chaine)->getValeur();
    if (typeid (*val) == typeid (ValeurEntiere))
        cout << ((ValeurEntiere*) val)->getValeur() << endl;
    else if (typeid (*val) == typeid (ValeurChaine))
        cout << ((ValeurChaine*) val)->getValeur() << endl;
    else if (typeid (*val) == typeid (ValeurReelle))
        cout << ((ValeurReelle*) val)->getValeur() << endl;
    

    return NULL;
}






